//
//  EzBase64.cpp
//  FFTEST_RTSP_TCP
//
//  Created by 刘跃龙 on 1/8/18.
//  Copyright © 2018 absolutemobile. All rights reserved.
//

#include "EzBase64.h"
#include <stdio.h>
#include <stdlib.h>

#ifdef  __cplusplus
extern "C" {
#endif
#include "base91.h"
#ifdef  __cplusplus
}
#endif

EZ_BEGIN

std::string base91Encode(std::string data){
    basE91 t_base91;
    basE91_init(&t_base91);
    size_t len = data.size() * 2 + 1;
    char buffer[len];
    memset(buffer,0,len);
    size_t pos = basE91_encode(&t_base91,data.c_str(),data.size(),buffer);
    basE91_encode_end(&t_base91,buffer+pos);
    return std::string((char*)buffer);
}
std::string base91Decode(std::string data){
    basE91 t_base91;
    basE91_init(&t_base91);
    size_t len = data.size();
    char buffer[len];
    memset(buffer,0,len);
    size_t pos = basE91_decode(&t_base91,data.c_str(),data.size(),buffer);
    basE91_decode_end(&t_base91, buffer+pos);
    return std::string((char*)buffer);
}

unsigned char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int _base64Decode(const unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len )
{
    static char inalphabet[256], decoder[256];
    int i, bits, c = 0, char_count, errors = 0;
    unsigned int input_idx = 0;
    unsigned int output_idx = 0;
    
    for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
        inalphabet[alphabet[i]] = 1;
        decoder[alphabet[i]] = i;
    }
    
    char_count = 0;
    bits = 0;
    for( input_idx=0; input_idx < input_len ; input_idx++ ) {
        c = input[ input_idx ];
        if (c == '=')
            break;
        if (c > 255 || ! inalphabet[c])
            continue;
        bits += decoder[c];
        char_count++;
        if (char_count == 4) {
            output[ output_idx++ ] = (bits >> 16);
            output[ output_idx++ ] = ((bits >> 8) & 0xff);
            output[ output_idx++ ] = ( bits & 0xff);
            bits = 0;
            char_count = 0;
        } else {
            bits <<= 6;
        }
    }
    
    if( c == '=' ) {
        switch (char_count) {
            case 1:
                fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
                errors++;
                break;
            case 2:
                output[ output_idx++ ] = ( bits >> 10 );
                break;
            case 3:
                output[ output_idx++ ] = ( bits >> 16 );
                output[ output_idx++ ] = (( bits >> 8 ) & 0xff);
                break;
        }
    } else if ( input_idx < input_len ) {
        if (char_count) {
            fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
                    ((4 - char_count) * 6));
            errors++;
        }
    }
    
    *output_len = output_idx;
    return errors;
}

void _base64Encode( const unsigned char *input, unsigned int input_len, char *output )
{
    unsigned int char_count;
    unsigned int bits;
    unsigned int input_idx = 0;
    unsigned int output_idx = 0;
    
    char_count = 0;
    bits = 0;
    for( input_idx=0; input_idx < input_len ; input_idx++ ) {
        bits |= input[ input_idx ];
        
        char_count++;
        if (char_count == 3) {
            output[ output_idx++ ] = alphabet[(bits >> 18) & 0x3f];
            output[ output_idx++ ] = alphabet[(bits >> 12) & 0x3f];
            output[ output_idx++ ] = alphabet[(bits >> 6) & 0x3f];
            output[ output_idx++ ] = alphabet[bits & 0x3f];
            bits = 0;
            char_count = 0;
        } else {
            bits <<= 8;
        }
    }
    
    if (char_count) {
        if (char_count == 1) {
            bits <<= 8;
        }
        
        output[ output_idx++ ] = alphabet[(bits >> 18) & 0x3f];
        output[ output_idx++ ] = alphabet[(bits >> 12) & 0x3f];
        if (char_count > 1) {
            output[ output_idx++ ] = alphabet[(bits >> 6) & 0x3f];
        } else {
            output[ output_idx++ ] = '=';
        }
        output[ output_idx++ ] = '=';
    }
    
    output[ output_idx++ ] = 0;
}

std::string base64Decode(std::string data)
{
    std::string ret = "";
    int padding = 4 - data.size()%4;
    if(padding==3)
    {
        printf("Base64Utils: padding error decoding");
        return "";
    }else if(padding>0){
        data.append(padding,'=');
    }
    
    unsigned int outLength = 0;
    unsigned char *out = (unsigned char*)malloc(data.size() * 3.0f / 4.0f + 1);
    int status = _base64Decode((unsigned char*)data.data(),(unsigned int)data.size(), out, &outLength);
    if (status > 0 )
    {
        printf("Base64Utils: error decoding");
        free(out);
        out = NULL;
        outLength = 0;
    }else{
        ret = std::string((char*)out,outLength);
    }
    free(out);
    return ret;
}

std::string base64Encode(std::string data) {
    std::string ret = "";
    if(data.empty()){
        return ret;
    }
//    unsigned int outLength = (unsigned int)data.size() * 4 / 3 + ((unsigned int)data.size() % 3 > 0 ? 4 : 0);
    unsigned int outLength = (unsigned int)data.size()%3 == 0 ? ((unsigned int)data.size()/3)*4 : ((unsigned int)data.size()/3+1)*4;//LYL modify
    char *out = (char*)malloc(outLength+4);
    _base64Encode((unsigned char*)data.data(), (unsigned int)data.size(), out);
    ret = std::string(out,outLength);
    free(out);
    return ret;
}

EZ_END
